#include "intersectbinding.h"

#include "../arc.h"
#include "../expression.h"
#include "../expressions/expressioncommon.h"
#include "../place.h"
#include "../transition.h"
#include "../types/colorset.h"
#include "../types/multiset.h"
#include "binding.h"
#include "bindingalgorithmhelper.h"
#include "simplebinding.h"

#include <QtCore/QHash>
#include <QtCore/QSet>

using namespace wfnet;

IntersectBinding::IntersectBinding() : IBindingAlgorithm()
{

}

IntersectBinding::~IntersectBinding()
{

}

bool IntersectBinding::bind(Binding& binding,
    const core::BasicTime& globalClock,
    core::BasicTime& proposedGlobalClock,
    const QSet<const Arc* >& inputArcs,
    const Transition* transition) const
{
    bool ret = true;

    QHash<VariableName, QSet<const Arc* > > varMapping;

    foreach (const Arc* arc, inputArcs)
    {
        Expression* expr = dynamic_cast<Expression* >(
            arc->getExpression());
        const QSet<VariableName> names =
            expr->getUserVariables().keys().toSet();

        foreach (const VariableName& var, names)
        {
            varMapping[var] << arc;
        }
    }

    proposedGlobalClock = 0;
    core::BasicTime _proposedGlobalClock;

    QHash<VariableName, QSet<const Arc* > >::const_iterator it =
        varMapping.constBegin();
    for (; it != varMapping.constEnd(); ++it)
    {
        const VariableName var = it.key();
        const QList<const Arc* > arcs = it.value().toList();

        if (arcs.size() > 1)
        {
            BasicTime _globalClock = globalClock;
            _globalClock.add(transition->getDelay().getTime());

            if (arcs[0]->getPlace()->getColorSet()->isTimed())
            {
                TimedMultiset intersectedMt;

                BAH->getInstersection(arcs, intersectedMt);

                if (!BAH->bindFromConstraint<TimedMultiset>(var,
                    intersectedMt, binding, _globalClock,
                    _proposedGlobalClock))
                {
                    break;
                }

                if (_proposedGlobalClock > proposedGlobalClock)
                {
                    proposedGlobalClock = _proposedGlobalClock;
                }
            }
            else
            {
                Multiset intersectedMt;

                BAH->getInstersection(arcs, intersectedMt);

                ret = BAH->bindFromConstraint<Multiset>(var, intersectedMt,
                    binding, _globalClock, _proposedGlobalClock);

                if (!ret)
                {
                    break;
                }

                if (_proposedGlobalClock > proposedGlobalClock)
                {
                    proposedGlobalClock = _proposedGlobalClock;
                }
            }
        }
        else
        {
            const Arc* arc = arcs[0];

            BasicTime _globalClock = globalClock;
            _globalClock.add(transition->getDelay().getTime());
            ret = BAH->bindFreeVariable(var, arc, binding, _globalClock,
                _proposedGlobalClock);

            if (!ret)
            {
                break;
            }

            if (_proposedGlobalClock > proposedGlobalClock)
            {
                proposedGlobalClock = _proposedGlobalClock;
            }
        }
    }

    return ret;
}
